/**
 * data-h2c-type="" 
 * image: data-h2c-src, data-h2c-text
 * view: data-h2c-text
 * button: data-h2c-text， data
 * 
 * data-h2c-class=""
 * data-h2c-style="{}"
 * fontSize, color, backgroundColor, border
 */

/*
<view 
  class="h2c-"
  style="color: #ff000; border: 1px solid black; background: red; border-radius: 10px; padding: 10px"
  :data-h2c-image="userInfo.headImg"
>
  <view
    class="h2c-"
    data-h2c-text="1111111111111122222222333333"
  >
    1111111111111122222222333333
    <view
      class="h2c-"
      data-h2c-text="222"
    >
      222
    </view>
  </view>
  <view
    class="h2c-"
    data-h2c-text="22"
  >
    22
  </view>
  <u-button
    class1="h2c-"
    data-h2c-text="123"
  >
    123
  </u-button>
</view>
<canvas 
  id="h2c-canvas"
  canvas-id="h2c-canvas"
  type="2d"
  style="width: 0; height: 0;"
/>
<image
  :src="idata"
  style="border: 1px solid black; width: 700rpx; height: 700rpx"
  mode="aspectFit"
/>

import {h2c} from '@/uni_modules/xone/utils/h2c'
let filepath = await h2c(this)
*/

let selector = '.h2c-'
let elInfos = []
let fieldKeys = {
  size: true,
  rect: true,
  dataset: true,
  properties: ['src', 'tagName'],
  computedStyle: ['color', 'textAlign', 'verticalAlign', 'fontSize', 'fontWeight', 'borderColor', 'borderWidth', 'backgroundColor', 'borderRadius', 'paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom'],
}

function getFields(el) {
  return new Promise((resolve,reject)=>{
    uni.createSelectorQuery().in(el).selectAll(selector).fields(fieldKeys, data => {
      resolve(data)
    }).exec()
  })
}
async function getH2cEls(el) {
  let info = await getFields(el)
  elInfos = [...elInfos,...info]


  for(let v of Object.values(el.$refs)){
    if(Array.isArray(v)) {
      for(let vv of v){
        await getH2cEls(vv)
      }
    }else{
      await getH2cEls(v)
    }
  }
  
  return elInfos
}
function getH2cEls1(el) {
  uni.createSelectorQuery().in(el).selectAll(selector).fields(fieldKeys, data => {
    elInfos = [...elInfos,...data]
    el.$refs.forEach(v=>{
      if(Array.isArray(v)) {
        v.forEach(vv=>{
          getH2cEls(vv)
        })
      }else{
        getH2cEls(v)
      }
    })
  }).exec()
}

export async function h2c(page, {
  styles = {},
  prefix = 'h2c-',
  canvasId = 'h2c-canvas',
} = {}) {
  console.log('h2c enter')
  let finish = false
  let els = []
  let i = 0

  els = await getH2cEls(page)
  

  // while (!finish) {
  //   await new Promise(r => {
  //     uni.createSelectorQuery().in(page).select('#' + prefix + i).fields({
  //       size: true,
  //       rect: true,
  //       dataset: true,
  //       properties: ['src', 'tagName'],
  //       computedStyle: ['color', 'borderColor', 'borderWidth', 'backgroundColor', 'borderRadius', 'paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom'],
  //     }, data => {
  //       if (data) {
  //         els.push(data)
  //         r()
  //         // setTimeout(() => r(), 50)
  //       } else {
  //         finish = true
  //         r()
  //       }
  //     }).exec()
  //   })
  //   i++
  // }

  // await new Promise(r => {
  //   uni.createSelectorQuery().in(page).selectAll('.' + prefix).fields({
  //     size: true,
  //     rect: true,
  //     dataset: true,
  //     properties: ['src', 'tagName'],
  //     computedStyle: ['color', 'textAlign', 'verticalAlign', 'fontSize', 'fontWeight', 'borderColor', 'borderWidth', 'backgroundColor', 'borderRadius', 'paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom'],
  //     context: true,
  //     node: true
  //   }, data => {
  //     els = data
  //     r()
  //   }).exec()
  // })
  // console.log('h2c els:', els)
  // await new Promise(r => {
  //   uni.createSelectorQuery().in(page.$refs.xcommchat).selectAll('.' + prefix).fields({
  //     size: true,
  //     rect: true,
  //     dataset: true,
  //     properties: ['src', 'tagName'],
  //     computedStyle: ['color', 'textAlign', 'verticalAlign', 'fontSize', 'fontWeight', 'borderColor', 'borderWidth', 'backgroundColor', 'borderRadius', 'paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom'],
  //     context: true,
  //     node: true
  //   }, data => {
  //     els = [...els,...data]
  //     r()
  //   }).exec()
  // })
  console.log('h2c els1:', els)
  
  // await new Promise(r => {
  //   uni.createSelectorQuery().in(page.$refs.xcommchat.$refs.xcommchatitem[0]).selectAll('.' + prefix).fields({
  //     size: true,
  //     rect: true,
  //     dataset: true,
  //     properties: ['src', 'tagName'],
  //     computedStyle: ['color', 'textAlign', 'verticalAlign', 'fontSize', 'fontWeight', 'borderColor', 'borderWidth', 'backgroundColor', 'borderRadius', 'paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom'],
  //     context: true,
  //     node: true
  //   }, data => {
  //     els = [...els,...data]
  //     r()
  //   }).exec()
  // })
  // console.log('h2c els2:', els)


  if (!els.length) return

  let canvas, ctx;
  // canvas = document.querySelector('#'+canvasId)
  // ctx = uni.createCanvasContext(canvasId, page)
  await new Promise(r => {
    const query = page.$refs.h2c.createSelectorQuery().select('#' + canvasId).fields({ node: true }).exec(res => {
      if (!res || !res.length || !res[0]) return r()
      canvas = res[0].node
      ctx = canvas.getContext('2d')
      r()
    })
  })

  console.log('h2c drwa:', canvas, ctx)
  if (!canvas || !ctx) return


  const systemInfo = uni.getSystemInfoSync();
  let pix = systemInfo.devicePixelRatio||1;

  ctx.scale(pix, pix);
  const w = els[0].width
  const h = els[0].height
  canvas.width = w
  canvas.height = h
  ctx.clearRect(0, 0, w * pix, h * pix)

  for (let i = els.length - 1; i >= 0; i--) {
    let el = els[i]
    if (i) {
      el.top = el.top - els[0].top
      el.right = els[0].right - el.right
      el.left = el.left - els[0].left
      el.bottom = els[0].bottom - el.bottom
    } else {
      el.top = 0
      el.right = el.left + el.width
      el.left = 0
      el.bottom = el.top + el.height
    }
  }
  for (let el of els) {
    await draw(canvas, ctx, el, styles)
  }

  console.log('h2c get file')
  let [error, res] = await uni.canvasToTempFilePath({
    x: 0,
    y: 0,
    width: w,
    height: h,
    destWidth: w * pix,
    destHeight: h * pix,
    canvas: canvas,
    fileType: 'png',
    quality: 1,
    canvasId,
  }, page)
  console.log('h2c end:', error, res)
  return error ? '' : res.tempFilePath
}

function roundRect(ctx, x, y, w, h, r, fill = true, stroke = false) {
  if (r < 0) return

  ctx.beginPath()
  ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2)
  ctx.arc(x + w - r, y + r, r, Math.PI * 3 / 2, 0)
  ctx.arc(x + w - r, y + h - r, r, 0, Math.PI / 2)
  ctx.arc(x + r, y + h - r, r, Math.PI / 2, Math.PI)
  ctx.lineTo(x, y + r)
  if (stroke) ctx.stroke()
  if (fill) ctx.fill()
}

function transUnit(unit = '', size) {
  if ((typeof unit === 'string') && unit.endsWith('%')) unit = Math.ceil(parseInt(unit.replace('%','')) * size / 100)
  if ((typeof unit === 'string') && unit.endsWith('px')) unit = unit.replace('px', '')
  if ((typeof unit === 'string') && unit.endsWith('em')) unit = Math.ceil(parseFloat(unit.replace('em')) * size)
  return Number.isNaN(unit) ? 0 : Number(unit)
}

function transColor(str) {
  if (str === 'transparent' || str === 'rgba(0, 0, 0, 0)') return 'transparent'
  if (str&&str[0]==='#') return str

  let reg = /^(rgb|RGB)/;
  if (!reg.test(str)) { return; }
  var arr = str.slice(4, str.indexOf(')')).split(",")
  let color = '#';
  for (var i = 0; i < arr.length; i++) {
    var t = Number(arr[i]).toString(16)
    if (t == "0") {   //如果为“0”的话，需要补0操作,否则只有5位数
      t = t + "0"
    }
    color += t;
  }
  return color;
}

async function draw(canvas, ctx, el = {}, styles = {}) {
  console.log('h2c drawf:', el, styles)

  let style = {}
  el.dataset.h2cClass && el.dataset.h2cClass.split(/\s/).forEach(v => {
    if (styles[v]) {
      Object.assign(style, styles[v])
    }
  })
  if (el.dataset.h2cStyle) Object.assign(style, el.dataset.h2cStyle)

  let {
    left: x, top: y, width: w, height: h,
    color = '#000',
    lineHeight = '1.4em',
    fontSize = 14,
    fontWeight = '',
    textAlign = 'left',
    verticalAlign = 'middle',
    borderRadius = 0,
    borderWidth = 0,
    borderColor,
    paddingTop = 0,
    paddingBottom = 0,
    paddingLeft = 0,
    paddingRight = 0,
    backgroundColor = 'transparent',
  } = {
    ...el,
    ...style
  }
  color = transColor(color)
  backgroundColor = transColor(backgroundColor)
  borderColor = transColor(borderColor)
  fontSize = transUnit(fontSize)
  lineHeight = transUnit(lineHeight, fontSize)
  borderRadius = transUnit(borderRadius, w)
  borderWidth = transUnit(borderWidth.slice(' ')[0], fontSize)
  paddingTop = transUnit(paddingTop, fontSize)
  paddingBottom = transUnit(paddingBottom, fontSize)
  paddingLeft = transUnit(paddingLeft, fontSize)
  paddingRight = transUnit(paddingRight, fontSize)


  ctx.save()
  // 外环
  if (borderWidth > 0) {
    ctx.strokeStyle = borderColor || color
    ctx.lineWidth = borderWidth
    ctx.lineCap = 'round'
    ctx.lineJoin = 'round'
    roundRect(ctx, x, y, w, h, borderRadius, false, true)
  }
  ctx.restore()


  // 内环
  x += borderWidth
  y += borderWidth
  w = w - 2 * borderWidth
  h = h - 2 * borderWidth
  ctx.save()
  ctx.fillStyle = backgroundColor
  // const innerWidth = w - 2 * borderWidth
  // const innerHeight = h - 2 * borderWidth
  // const innerRadius = borderRadius - borderWidth >= 0 ? borderRadius - borderWidth : 0
  // roundRect(ctx, x + borderWidth, y + borderWidth, innerWidth, innerHeight, innerRadius, true, false)
  roundRect(ctx, x, y, w, h, borderRadius, true, false)
  ctx.restore()

  let imageSrc = el.dataset.h2cImage
  if (imageSrc) {
    await new Promise(r => {
      ctx.save()
      roundRect(ctx, x, y, w, h, borderRadius, false, false)
      ctx.clip()
      const img = canvas.createImage()
      img.onload = () => {
        ctx.drawImage(img, x, y, w, h)
        ctx.restore()
        r()
      }
      img.onerror = () => {
        ctx.restore()
        r()
      }
      img.src = imageSrc
    })
  }

  x += paddingLeft
  y += paddingTop
  w = w - paddingLeft - paddingRight
  h = h - paddingTop - paddingBottom


  let text = el.dataset.h2cText
  if (text) {
    ctx.save()
    ctx.textBaseline = 'top'
    ctx.font = `${fontWeight} ${fontSize}px sans-serif`
    ctx.textAlign = textAlign
    ctx.fillStyle = color

    // 水平布局
    switch (textAlign) {
      case 'left':
        break
      case 'center':
        x += 0.5 * w
        break
      case 'right':
        x += w
        break
      default: break
    }

    const textWidth = ctx.measureText(text).width
    const actualHeight = Math.ceil(textWidth / w) * lineHeight
    let paddingTop = Math.ceil((h - actualHeight) / 2)
    if (paddingTop < 0) paddingTop = 0

    // 垂直布局
    switch (verticalAlign) {
      case 'top':
        break
      case 'middle':
        y += paddingTop
        break
      case 'bottom':
        y += 2 * paddingTop
        break
      default: break
    }

    const inlinePaddingTop = Math.ceil((lineHeight - fontSize) / 2)

    // 不超过一行
    if (textWidth <= w) {
      ctx.fillText(text, x, y + inlinePaddingTop)
      return
    }

    // 多行文本
    const chars = text.split('')
    const _y = y

    // 逐行绘制
    let line = ''
    for (const ch of chars) {
      const testLine = line + ch
      const testWidth = ctx.measureText(testLine).width

      if (testWidth > w) {
        ctx.fillText(line, x, y + inlinePaddingTop)
        y += lineHeight
        line = ch
        if ((y + lineHeight) > (_y + h)) break
      } else {
        line = testLine
      }
    }

    // 避免溢出
    if ((y + lineHeight) <= (_y + h)) {
      ctx.fillText(line, x, y + inlinePaddingTop)
    }
    ctx.restore()
  }

  console.log('h2c draw end')
}